---@module w5500
module(..., package.seeall)

require "misc"
require "pins"
require "dns"

local debug_flag = "W5500"
pmd.ldoset(15, pmd.LDO_VMMC)
local reset_pin = pins.setup(pio.P0_27, 1)
local cs_pin = pins.setup(pio.P0_28, 1)
MAC = ""
local UDP_SOCK = { false, false, false, false }   --使用udp的专用socket
local UDP_PORT = 70     --udp的本地端口号,每次请求+1
local domain_cache = {}     --域名缓存

isLineLink = false -- 是否插入了网线
------------------------------------芯片基础操作-----------------------------------------
---@param addr number @寄存器地址
---@param len number @要读取的字节数
---@param sn number @读哪个socket寄存器
---@param cacheMode boolean @是否读socket缓存
---@return string @读取到的数据
local function readBuf(addr, len, sn, cacheMode)
    local r
    cs_pin(0)
    if sn then
        --需要指定那个socket
        if cacheMode then
            --读缓存
            if sn == 1 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x18) .. string.rep("\255", len))
            elseif sn == 2 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x38) .. string.rep("\255", len))
            elseif sn == 3 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x58) .. string.rep("\255", len))
            elseif sn == 4 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x78) .. string.rep("\255", len))
            elseif sn == 5 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x98) .. string.rep("\255", len))
            elseif sn == 6 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xB8) .. string.rep("\255", len))
            elseif sn == 7 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xD8) .. string.rep("\255", len))
            elseif sn == 8 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xF8) .. string.rep("\255", len))
            end
        else
            -- 选择socket寄存器
            if sn == 1 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x08) .. string.rep("\255", len))
            elseif sn == 2 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x28) .. string.rep("\255", len))
            elseif sn == 3 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x48) .. string.rep("\255", len))
            elseif sn == 4 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x68) .. string.rep("\255", len))
            elseif sn == 5 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x88) .. string.rep("\255", len))
            elseif sn == 6 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xA8) .. string.rep("\255", len))
            elseif sn == 7 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xC8) .. string.rep("\255", len))
            elseif sn == 8 then
                r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0xE8) .. string.rep("\255", len))
            end
        end
    else
        --选择通用寄存器
        r = spi.send_recv(0, pack.pack(">H", addr) .. string.char(0x00) .. string.rep("\255", len))
    end
    cs_pin(1)
    return r:sub(4, -1)
end

---@param addr number @寄存器地址
---@param data string @要写的数据
---@param sn number @写哪个socket寄存器
---@param cacheMode boolean @是否写socket缓存
---@return number @写入的数据长度
local function writeBuf(addr, data, sn, cacheMode)
    local len
    cs_pin(0)
    if sn then
        --需要指定那个socket
        if cacheMode then
            --写缓存
            if sn == 1 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x14) .. data)
            elseif sn == 2 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x34) .. data)
            elseif sn == 3 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x54) .. data)
            elseif sn == 4 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x74) .. data)
            elseif sn == 5 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x94) .. data)
            elseif sn == 6 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xB4) .. data)
            elseif sn == 7 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xD4) .. data)
            elseif sn == 8 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xF4) .. data)
            end
        else
            -- 选择socket寄存器
            if sn == 1 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x0C) .. data)
            elseif sn == 2 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x2C) .. data)
            elseif sn == 3 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x4C) .. data)
            elseif sn == 4 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x6C) .. data)
            elseif sn == 5 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0x8C) .. data)
            elseif sn == 6 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xAC) .. data)
            elseif sn == 7 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xCC) .. data)
            elseif sn == 8 then
                len = spi.send(0, pack.pack(">H", addr) .. string.char(0xEC) .. data)
            end
        end
    else
        --选择通用寄存器
        len = spi.send(0, pack.pack(">H", addr) .. string.char(0x04) .. data)
    end
    cs_pin(1)
    return len
end

--检查网线是否插入
---@return boolean
function phyCheck()
    local phy_connect = readBuf(0x002E, 1)
    log.info(debug_flag, "PHY status: ", string.toHex(phy_connect, ":"))
    if bit.band(string.byte(phy_connect), 0x02) == 0x02 then
        log.info(debug_flag, "PHY SPEED IS 100M")
    else
        log.info(debug_flag, "PHY SPEED IS 10M")
    end
    if bit.band(string.byte(phy_connect), 0x01) == 0x01 then
        log.info(debug_flag, "PHY IS LINKED!")
        isLineLink = true
        return true
    else
        log.info(debug_flag, "PHY IS OFFLINKED!")
        isLineLink = false
        return false
    end
end

--设置mac地址
function setMac()
    local imei = misc.getImei():sub(11, -1)
    writeBuf(0x0009, string.char(0x88) .. imei)
    local mac = readBuf(0x0009, 6)
    log.info(debug_flag, "NOW MAC: ", string.toHex(mac, ":"))
    MAC = string.toHex(mac, ":")
end
--读取mac地址
function getMac()
    return readBuf(0x0009, 6)
end
--设置网卡信息
function setNetInfo(gateway, mask, ip)
    writeBuf(0x0001, gateway)
    writeBuf(0x0005, mask)
    writeBuf(0x000f, ip)

    local GateWay = readBuf(0x0001, 4)
    local Mask = readBuf(0x0005, 4)
    local Ip = readBuf(0x000f, 4)
    log.info(debug_flag, "GateWay : ", pack.unpack(GateWay, "b6"))
    log.info(debug_flag, "Mask : ", pack.unpack(Mask, "b4"))
    log.info(debug_flag, "Ip : ", pack.unpack(Ip, "b4"))
end

--设置8个Socket的发送接收缓存大小,每个各2k
local function setSocketsCaches()
    --4个接收缓冲区大小
    writeBuf(0x001E, string.char(2), 1)
    writeBuf(0x001E, string.char(2), 2)
    writeBuf(0x001E, string.char(2), 3)
    writeBuf(0x001E, string.char(2), 4)
    writeBuf(0x001E, string.char(2), 5)
    writeBuf(0x001E, string.char(2), 6)
    writeBuf(0x001E, string.char(2), 7)
    writeBuf(0x001E, string.char(2), 8)
    --4个发送缓冲区大小
    writeBuf(0x001F, string.char(2), 1)
    writeBuf(0x001F, string.char(2), 2)
    writeBuf(0x001F, string.char(2), 3)
    writeBuf(0x001F, string.char(2), 4)
    writeBuf(0x001F, string.char(2), 5)
    writeBuf(0x001F, string.char(2), 6)
    writeBuf(0x001F, string.char(2), 7)
    writeBuf(0x001F, string.char(2), 8)
end

-- 重启phy
function rest_phy()
    writeBuf(0x0000, string.char(0x80))
    setMac()
end

------------------------------------socket相关操作-----------------------------------------
---TCP
local PORT = 0xC000    --client端默认端口号
--socket状态
local socket_status = {
    [0x00] = "SOCK_CLOSED",
    [0x13] = "SOCK_INIT",
    [0x15] = "SOCK_SYNSENT",
    [0x16] = "SOCK_SYNRECV",
    [0x17] = "SOCK_ESTABLISHED",
    [0x1C] = "SOCK_CLOSE_WAIT",
    [0x18] = "SOCK_FIN_WAIT",
    [0x22] = "SOCK_UDP",
}

--4个socket通道的使用状态
local socket_used = { false, false, false, false }
--4个Socket的连接状态
local socketc_connect = { false, false, false, false }
--是否允许socket状态机轮询
local alow_run = true
--是否运行状态机查询任务
local alow_kill = false

--等待设置socket控制寄存器确认
---@param sn number @socket ID
---@param times number @等待次数
---@return boolean
local function waitSetSocketOk(sn, times)
    while times > 0 do
        if string.byte(readBuf(0x0001, 1, sn)) == 0 then
            return true
        else
            times = times - 1
        end
        sys.wait(5)
    end
    return false
end

--获取指定的socket状态
---@param sn number @socket ID
---@param flag string @等待的状态，在socket_status中
---@param timeout number @等待时间 1单位为 100ms
---@return boolean
local function getSocketStatusWait(sn, flag, times)
    local read, status
    while times > 0 do
        read = string.byte(readBuf(0x0003, 1, sn))
        status = socket_status[read]
        if status and status == flag then
            return true
        else
            times = times - 1
        end
        sys.wait(5)
    end
    return false
end

--获取当前socket的状态
---@param sn number @socket ID
---@return string
local function getSocketStatus(sn)
    local read = string.byte(readBuf(0x0003, 1, sn))
    local status = socket_status[read]
    if status then
        return status
    else
        return "NULL"
    end
end

--获取socket接收缓冲区数据大小
---@param sn number @socket ID
---@return number
local function getNumFromRecv(sn)
    local _, receiveSize = pack.unpack(readBuf(0x0026, 2, sn), ">H")
    return receiveSize
end

--判断socket发送是否完成
---@param sn number @socket ID
---@return boolean
local function isSendEnd(sn)
    local IR = string.byte(readBuf(0x0002, 1, sn))
    if bit.band(IR, 0x10) == 0x10 then
        --清除已发送完成中断
        writeBuf(0x0002, string.char(0x10), sn)
        return true
    else
        return false
    end
end

-- 等待发送完成
local function waitSendOk(sn, needToClose)
    local IR = string.byte(readBuf(0x0002, 1, sn))
    -- 等待发送完成
    while bit.band(IR, 0x10) ~= 0x10 do
        IR = string.byte(readBuf(0x0002, 1, sn))
        if bit.band(IR, 0x08) == 0x08 then
            --TIMEOUT
            --清除已发送完成和超时中断
            writeBuf(0x0002, string.char(0x18), sn)
            log.warn(debug_flag, "Send data timeoout , sn: " .. sn)
            if needToClose then
                --关闭socket
                writeBuf(0x0001, string.char(0x10), sn)
                --查询是否发送成功
                if not waitSetSocketOk(sn, 20) then
                    log.warn(debug_flag, "Set socket error! sn = " .. sn)
                    return false
                end
            end
            return false
        end
        sys.wait(10)
    end

    --清除已发送完成中断
    writeBuf(0x0002, string.char(0x10), sn)

    if needToClose then
        --关闭socket
        writeBuf(0x0001, string.char(0x10), sn)
        --查询是否发送成功
        if not waitSetSocketOk(sn, 20) then
            log.warn(debug_flag, "Set socket error! sn = " .. sn)
            return false
        end
    end
    return true
end

--关闭socket通道
---@param sn number @socket端口号，1~4
---@return boolean
function close(sn)
    if sn > 4 or sn < 1 then
        log.warn(debug_flag, "socket number error! " .. sn)
        return false
    end
    log.warn(debug_flag, "To close socket " .. sn)
    if getSocketStatus(sn) ~= "SOCK_CLOSED" then
        --关闭socket
        writeBuf(0x0001, string.char(0x10), sn)
        --查询是否发送成功
        if not waitSetSocketOk(sn, 20) then
            log.warn(debug_flag, "Set socket error! sn = " .. sn)
            return false
        end
        --清除socket中断寄存器
        writeBuf(0x0002, string.char(0xff), sn)
    else
        local msg = {
            socket_index = sn,
            type = "",
            result = 0,
        }
        if socket_used[i] then
            socketc_connect[i] = false
            socket_used[i] = false
        end
        msg.type = "SOCKET_CLOSE"
        sys.publish("W5100S_SOCKET_CLOSE", msg)
        log.warn(debug_flag, "socket number " .. sn .. " close ok!")
    end
    return true
end

---@param sn number @socket号
local function udpClose(sn)
    writeBuf(0x0001, string.char(0x10), sn)
    --查询是否发送成功
    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Close udp socket error!", sn)
        return false
    end
    return true
end

--建立socket通道
---@param sn number @socket端口号，1~4
---@param protocol string @协议族 暂时只支持"TCP"
---@return boolean
function socket(sn, protocol)
    if sn > 4 or sn < 1 then
        log.warn(debug_flag, "socket number error! " .. sn)
        return false
    end
    --close(sn)
    --设置MR, 为TCP模式
    writeBuf(0x0000, string.char(0x01), sn)
    --设置源端口
    writeBuf(0x0004, string.char(bit.rshift(PORT, 8), bit.band(PORT, 0x00ff)), sn)
    PORT = PORT + 1
    if PORT == 0xFFF0 then
        PORT = 0xC000
    end
    --open socket
    writeBuf(0x0001, string.char(0x01), sn)
    if waitSetSocketOk(sn, 20) then
        socket_used[sn] = true
        return true
    else
        log.warn(debug_flag, "Set socket error! sn = " .. sn)
        return false
    end
end

--连接socket服务器
---@param addr string @服务器地址，不支持域名
---@param port number @服务器端口号
---@param id number @是否指定socket端口，没有指定自动分配
---@return number @返回连接成功的socket id号
function connect(addr, port, id)
    local sn
    if id then
        sn = id
    else
        --分配未使用的socket通道
        for k, v in ipairs(socket_used) do
            if not v then
                sn = k
                break
            end
        end
        if not sn then
            log.warn(debug_flag, "There is no vaild socket channel")
            return nil
        end
    end

    if not socket(sn, "TCP") then
        log.warn(debug_flag, "Socket " .. sn .. " init error!")
        return nil
    end

    --建立连接任务，成功后publish "W5100S_SOCKET_CONNECT"

    if not getSocketStatusWait(sn, "SOCK_INIT", 50) then
        log.warn(debug_flag, "Socket " .. sn .. " is not ready!")
        return nil
    end
    --设置目标IP地址
    local ip = addr:split(".")

    -- 传入addr为域名格式
    if not (tonumber(ip[1]) and tonumber(ip[2]) and tonumber(ip[3]) and tonumber(ip[4])) then
        -- 先从缓存中查找ip
        for k, v in ipairs(domain_cache) do
            if v.ADDR == addr then
                ip = v.IP
                log.info(debug_flag, "Using domain cache for " .. addr, "ip:", ip[1], ip[2], ip[3], ip[4])
                break
            end
        end

        --是否需要进一步请求dns
        if not (tonumber(ip[1]) and tonumber(ip[2]) and tonumber(ip[3]) and tonumber(ip[4])) then
            local recv_data = doUdpAction(UDP_PORT, dns.dns_package(addr), nvm.get("DNS"), 53)
            if recv_data then
                local ip_table = dns.parseDns(recv_data)
                if #ip_table > 0 then
                    ip = ip_table[1]:split(".")
                    log.info(debug_flag, "Obtain ip from " .. addr, ip_table[1])
                    table.insert(domain_cache, {ADDR = addr, IP = ip})
                    if #domain_cache > 10 then
                        table.remove(domain_cache, 1)   --防止缓存太大
                    end
                else
                    log.warn(debug_flag, "Cant obtain ip from: ", addr)
                    return nil
                end
            else
                log.warn(debug_flag, "Cant obtain ip from: ", addr)
                return nil
            end
        end
    end

    writeBuf(0x000c, string.char(tonumber(ip[1]), tonumber(ip[2]), tonumber(ip[3]), tonumber(ip[4])), sn)
    --设置目标端口号
    writeBuf(0x0010, string.char(bit.rshift(port, 8), bit.band(port, 0x00ff)), sn)
    --connect to target
    writeBuf(0x0001, string.char(0x04), sn)
    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Connect socket error! sn = " .. sn)
        return nil
    end
    return sn
end

--断开socket连接
---@param sn number @socket端口号，1~4
---@return boolean
function disconnect(sn)
    --disconnect
    writeBuf(0x0001, string.char(0x08), sn)
    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Set socket disconnect error! sn = " .. sn)
        return false
    end
    if getSocketStatusWait(sn, "SOCK_CLOSED", 50) then
        log.info(debug_flag, "Disconnect socket " .. sn .. " ok!")
        socketc_connect[sn] = false
        return true
    else
        log.warn(debug_flag, "Disconnect socket " .. sn .. " failed!")
        return false
    end
end

--发送socket数据
---@param sn number @socket端口号，1~4
---@param data string @要发送的数据
---@return boolean
function send(sn, data)
    local sendBuf
    --最大发送缓冲区是2k
    if #data > 2048 then
        sendBuf = data:sub(1, 2048)
    else
        sendBuf = data
    end

    --判断socket是否在可发送状态
    if getSocketStatus(sn) ~= "SOCK_ESTABLISHED" and getSocketStatus(sn) ~= "SOCK_CLOSE_WAIT" then
        log.warn(debug_flag, "Socket " .. sn .. " cant send datas!" .. getSocketStatus(sn))
        return false
    end

    --等待socket空闲发送缓存的大小足够发送一次的长度 FSR
    local remain
    while true do
        _, remain = pack.unpack(readBuf(0x0020, 2, sn), ">H")
        if remain >= #sendBuf then
            break
        end
        sys.wait(5)
    end

    --获取发送写指针寄存器 TX_WR
    local _, ptr = pack.unpack(readBuf(0x0024, 2, sn), ">H")
    --写入发送区
    writeBuf(ptr, sendBuf, sn, true)

    --计算新的当前写入地址,写入TX_WR
    ptr = ptr + string.len(sendBuf)
    if ptr > 65535 then
        ptr = ptr - 65536
    end
    writeBuf(0x0024, string.char(bit.rshift(ptr, 8), bit.band(ptr, 0x00ff)), sn)

    --发送send命令
    writeBuf(0x0001, string.char(0x20), sn)

    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Send data failed! sn = " .. sn)
        return false
    end

    return true
end

--接收socket数据
---@param sn number @socket端口号，1~4
---@param len number @要接收的字节数
---@return string @接收到的数据
function recv(sn, len)
    local receiveSize
    while true do
        --获取接收缓冲区大小 RX_RSR
        receiveSize = getNumFromRecv(sn)
        if receiveSize ~= 0 then
            break
        end
        sys.wait(5)
    end
    --最大可接收数
    if receiveSize < len then
        len = receiveSize
    end

    --获取接收读指针寄存器 RX_RD
    local _, ptr = pack.unpack(readBuf(0x0028, 2, sn), ">H")
    --一次性读取
    local result = readBuf(ptr, len, sn, true)

    --计算新的当前读取地址,写入RX_RD
    ptr = ptr + len
    if ptr > 65535 then
        ptr = ptr - 65536
    end
    writeBuf(0x0028, string.char(bit.rshift(ptr, 8), bit.band(ptr, 0x00ff)), sn)
    --发送recv命令
    writeBuf(0x0001, string.char(0x40), sn)
    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Recv data failed! sn = " .. sn)
        return nil
    end

    --log.info(debug_flag, "Socket " .. sn .. " recv data:" .. recvData)
    return result
end

------------------------------------------------------------------------------------
---UDP
--通过udp发送数据
---@param sn number @socket号
---@param sourcePort number @本地端口
---@param data string @要发送的数据
---@param addr string @发送的IP地址 如“8.8.8.8”
---@param port number @目标端口
---@param needToClose boolean @发送数据后，是否自动关闭socket
local function sendToUdp(sn, sourcePort, data, addr, port, needToClose)
    local sendBuf
    --最大发送缓冲区是2k
    if #data > 2048 then
        sendBuf = data:sub(1, 2048)
    else
        sendBuf = data
    end

    -- 先判断udp_sock 的状态
    if getSocketStatus(sn) ~= "SOCK_CLOSED" then
        log.warn(debug_flag, "Udp socket " .. sn .. " cant send datas! " .. getSocketStatus(sn))
        return false
    end

    -- 建立一个udp
    writeBuf(0x0000, string.char(0x02), sn)
    --设置源端口
    writeBuf(0x0004, string.char(bit.rshift(sourcePort, 8), bit.band(sourcePort, 0x00ff)), sn)

    --打开socket
    writeBuf(0x0001, string.char(0x01), sn)
    if waitSetSocketOk(sn, 20) == false then
        log.warn(debug_flag, "Open udp socket error!", sn)
        return false
    end

    -- 判断是否在udp模式下
    if getSocketStatus(sn) ~= "SOCK_UDP" then
        log.warn(debug_flag, "Open udp socket error!", sn)
        return false
    end

    UDP_SOCK[sn - 4] = true     --设置该socket号为使用状态

    -- 写入目标地址和端口号
    local ip = addr:split(".")
    writeBuf(0x000c, string.char(tonumber(ip[1]), tonumber(ip[2]), tonumber(ip[3]), tonumber(ip[4])), sn)
    --设置目标端口号
    writeBuf(0x0010, string.char(bit.rshift(port, 8), bit.band(port, 0x00ff)), sn)

    --获取发送写指针寄存器 TX_WR
    local _, ptr = pack.unpack(readBuf(0x0024, 2, sn), ">H")
    --写入发送区
    writeBuf(ptr, sendBuf, sn, true)

    --计算新的当前写入地址,写入TX_WR
    ptr = ptr + string.len(sendBuf)
    if ptr > 65535 then
        ptr = ptr - 65536
    end
    writeBuf(0x0024, string.char(bit.rshift(ptr, 8), bit.band(ptr, 0x00ff)), sn)

    --发送send命令
    writeBuf(0x0001, string.char(0x20), sn)

    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Send data to udp socket failed!", sn)
        return false
    end
    -- 等待发送完成
    return waitSendOk(sn, needToClose)
end

--通过udp接收数据
---@param sn number @socket号
---@param timeout number @ 等待超时时间，ms， 默认3000ms
local function recvFromUdp(sn, timeout)
    -- 判断是否在udp模式下
    if getSocketStatus(sn) ~= "SOCK_UDP" then
        log.warn(debug_flag, "Open udp socket error!", sn)
        return nil
    end

    local waitCnt = timeout and timeout or 30
    local receiveSize
    while true do
        --获取接收缓冲区大小 RX_RSR
        receiveSize = getNumFromRecv(sn)
        if receiveSize > 8 then
            break
        end
        sys.wait(100)
        waitCnt = waitCnt - 1
        if waitCnt < 0 then
            log.warn(debug_flag, "Udp recv timeout!", sn)
            return nil
        end
    end

    --获取接收读指针寄存器 RX_RD
    local _, ptr = pack.unpack(readBuf(0x0028, 2, sn), ">H")
    -- 先读8个字节
    local head = readBuf(ptr, 8, udp_sock, true)
    ptr = ptr + 8
    if ptr > 65535 then
        ptr = ptr - 65536
    end
    -- 这里和文档并不一致，读回来的数据并不是 远程 ip（1-4） + 端口（5-6） + udp有效数据字长（7-8），很奇怪
    --log.info(debug_flag, "Recv udp : ", string.toHex(head, "-"))

    -- 再读剩下字节
    local data = readBuf(ptr, receiveSize - 8, sn, true)
    --计算新的当前读取地址,写入RX_RD
    ptr = ptr + (receiveSize - 8)
    if ptr > 65535 then
        ptr = ptr - 65536
    end
    writeBuf(0x0028, string.char(bit.rshift(ptr, 8), bit.band(ptr, 0x00ff)), sn)
    --发送recv命令
    writeBuf(0x0001, string.char(0x40), sn)
    if not waitSetSocketOk(sn, 20) then
        log.warn(debug_flag, "Recv data failed! sn = " .. sn)
        return nil
    end
    return data
end

-- 发起一次udp请求和接收数据
---@param sourcePort number @本地端口
---@param data string @要发送的数据
---@param addr string @发送的IP地址 如“8.8.8.8”
---@param port number @目标端口
---@param needToClose boolean @发送数据后，是否自动关闭socket
function doUdpAction(sourcePort, data, addr, port, needToClose)
    local sn
    --分配未使用的socket通道
    for k, v in ipairs(UDP_SOCK) do
        if v == false then
            sn = k + 4  -- udp 取 5、6、7、8
            break
        end
    end
    if not sn then
        log.warn(debug_flag, "There is no vaild udp socket")
        return nil
    end

    local result = sendToUdp(sn, sourcePort, data, addr, port, needToClose)

    UDP_PORT = sourcePort + 1
    if UDP_PORT > 100 then
        UDP_PORT = 70
    end

    local recv_data
    if needToClose then
        -- 直接返回结果
        UDP_SOCK[sn - 4] = false     --清空该socket号的使用状态
        return result
    else
        --等待接收数据
        if result == true then
            recv_data = recvFromUdp(sn)
        end
        udpClose(sn)
        UDP_SOCK[sn - 4] = false     --清空该socket号的使用状态
    end

    return recv_data    -- 返回接收数据
end

--ntp的时间服务器
local timeServer = {
    "cn.pool.ntp.org",
    "edu.ntp.org.cn",
    "cn.ntp.org.cn",
    "s2c.time.edu.cn",
    "time1.aliyun.com",
    "tw.pool.ntp.org",
    "0.cn.pool.ntp.org",
    "0.tw.pool.ntp.org",
    "1.cn.pool.ntp.org",
    "1.tw.pool.ntp.org",
    "3.cn.pool.ntp.org",
    "3.tw.pool.ntp.org",
}
local sbyte, ssub = string.byte, string.sub
-- ntp获取
function w_ntp()
    local recv_data
    local ip_table = {}
    for i = 1, #timeServer do
        -- 先解析服务器的域名ip
        recv_data = doUdpAction(UDP_PORT, dns.dns_package(timeServer[i]), nvm.get("DNS"), 53)
        if recv_data then
            ip_table = dns.parseDns(recv_data)
            if #ip_table > 0 then
                log.info("W5500 NTP", "Obtain ip from " .. timeServer[i], ip_table[1])
            else
                log.warn("W5500 NTP", "Cant obtain ip from: ", timeServer[i])
            end
        else
            log.warn("W5500 NTP", "Cant obtain ip from: ", timeServer[i])
        end

        if #ip_table > 0 then
            recv_data = doUdpAction(UDP_PORT, string.fromHex("E30006EC0000000000000000314E31340000000000000000000000000000000000000000000000000000000000000000"), ip_table[1], 123)

            if recv_data and #recv_data == 48 then
                local ntim = os.date("*t", (sbyte(ssub(recv_data, 41, 41)) - 0x83) * 2 ^ 24 + (sbyte(ssub(recv_data, 42, 42)) - 0xAA) * 2 ^ 16 + (sbyte(ssub(recv_data, 43, 43)) - 0x7E) * 2 ^ 8 + (sbyte(ssub(recv_data, 44, 44)) - 0x80) + 1)
                misc.setClock(ntim, function(time, result)
                    if result then
                        log.info("W5500 NTP", "Set ntp ok!")
                        tool.print_dump(time)
                        sys.publish("W5500_NTP_OK")
                    else
                        log.info("W5500 NTP", "Set ntp failed!")
                    end
                end)
                return
            else
                log.warn("W5500 NTP", "Get ntp failed!")
            end
        end
    end
end

--socke事件状态机
local function socketStateHandle()
    if alow_run then
        local status, receiveSize
        local msg = {
            socket_index = 0,
            type = "",
            result = 0,
            recv_len = 0
        }
        for i = 1, 4 do
            if socket_used[i] then
                status = getSocketStatus(i)
                msg.socket_index = i

                if status == "SOCK_ESTABLISHED" then
                    if not socketc_connect[i] then
                        socketc_connect[i] = true
                        msg.type = "SOCKET_CONNECT"
                        sys.publish("W5100S_SOCKET_CONNECT", msg)
                    else
                        --处理接收数据
                        receiveSize = getNumFromRecv(i)
                        if receiveSize and receiveSize > 0 then
                            msg.type = "SOCKET_RECV"
                            msg.recv_len = receiveSize
                            sys.publish("W5100S_SOCKET_RECV", msg)
                        end

                        --判断发送完成
                        if isSendEnd(i) then
                            msg.type = "SOCKET_SEND"
                            sys.publish("W5100S_SOCKET_SEND", msg)
                        end
                    end
                elseif status == "SOCK_CLOSED" then
                    if socket_used[i] then
                        socketc_connect[i] = false
                        socket_used[i] = false
                        msg.type = "SOCKET_CLOSE"
                        sys.publish("W5100S_SOCKET_CLOSE", msg)
                        log.warn(debug_flag, "socket number " .. i .. " close ok!")
                    end
                else

                end
                sys.wait(5)
            end
        end
    end
end

--建立socket状态机任务
local function tcpTask()
    alow_kill = false
    sys.taskInit(function()
        while not alow_kill do
            socketStateHandle()
            sys.wait(5)
        end
        log.warn(debug_flag, "Socket task killed!")
    end)
end

--初始化w5500
---@return boolean @false os true
function init()
    log.info(debug_flag, "Init w5500...")
    --强制关闭状态机任务
    alow_kill = true
    --重启w5500
    reset_pin(0)
    sys.wait(100)
    reset_pin(1)
    sys.wait(1000)
    socket_used = { false, false, false, false }
    socketc_connect = { false, false, false, false }

    if not spi.setup(spi.SPI_1, 0, 0, 8, 6500000, 1) then
        log.error(debug_flag, "spi setup failed")
        return false
    end

    --设置网卡
    local ip = nvm.get("IP")
    local gateway = nvm.get("GATEWAY")
    local subnetMask = nvm.get("MASK")
    setMac()
    setNetInfo(string.char(unpack(gateway)), string.char(unpack(subnetMask)), string.char(unpack(ip)))
    --setSocketsCaches()
    sys.taskInit(function()
        sys.wait(1000)
        phyCheck()
    end)

    tcpTask()
end

-- 查询当前是否有socket通道在用
---@return boolean
function isSocketRunnig()
    for _, v in ipairs(socket_used) do
        if v then
            return true
        end
    end
    return false
end

--socket状态机启停
---@param bool boolean @ true :启用  false： 停用
function socketRunning(bool)
    if bool == "kill" then
        alow_kill = true
    else
        alow_run = bool
    end
end